package auth

import (
	"errors"
	"fmt"
	"os"
	"sync"
	"time"

	"gitee.com/wangshh_123/cross-pay-go/src/config"

	"github.com/golang-jwt/jwt/v5"
)

var (
	ErrInvalidToken         = errors.New("invalid token")
	ErrExpiredToken         = errors.New("token has expired")
	ErrInvalidSigningMethod = errors.New("invalid signing method")

	// Cache for JWT claims
	tokenCache         sync.Map
	tokenCacheDuration = 1 * time.Minute
)

// getJWTSecret returns the JWT secret from environment variable or a default value
func getJWTSecret() string {
	if secret := os.Getenv("JWT_SECRET"); secret != "" {
		return secret
	}
	return "your-secret-key-here" // Default secret, should be overridden in production
}

// Claims 自定义JWT声明
type Claims struct {
	UserID    int64  `json:"user_id"`
	Username  string `json:"username"`
	RoleID    int64  `json:"role_id"`
	IsDeleted bool   `json:"is_deleted"`
	jwt.RegisteredClaims
}

// PreLoginClaims 预登录令牌的声明
type PreLoginClaims struct {
	UserID   int64  `json:"user_id"`
	Username string `json:"username"`
	jwt.RegisteredClaims
}

// GenerateToken 生成JWT令牌
func GenerateToken(userID int64, username string, roleID int64) (string, error) {
	// 设置过期时间
	expirationTime := time.Now().Add(24 * time.Hour)
	issuedAt := time.Now()
	notBefore := issuedAt

	// 创建JWT声明
	claims := &Claims{
		UserID:    userID,
		Username:  username,
		RoleID:    roleID,
		IsDeleted: false,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(expirationTime),
			IssuedAt:  jwt.NewNumericDate(issuedAt),
			NotBefore: jwt.NewNumericDate(notBefore),
			Issuer:    "cross-pay-go",
			Subject:   fmt.Sprintf("%d", userID),
		},
	}

	// 创建token
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

	// 使用密钥签名token
	tokenString, err := token.SignedString([]byte(getJWTSecret()))
	if err != nil {
		return "", fmt.Errorf("failed to sign token: %v", err)
	}

	return tokenString, nil
}

// ValidateToken 验证JWT令牌
func ValidateToken(tokenString string) (*Claims, error) {
	// 尝试从缓存获取
	if cached, ok := tokenCache.Load(tokenString); ok {
		cacheData := cached.(struct {
			Claims *Claims
			Expiry time.Time
		})
		if time.Now().Before(cacheData.Expiry) {
			return cacheData.Claims, nil
		}
	}

	// 解析token
	token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
		// 验证签名方法
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, ErrInvalidSigningMethod
		}
		return []byte(getJWTSecret()), nil
	})

	if err != nil {
		if errors.Is(err, jwt.ErrTokenExpired) {
			return nil, ErrExpiredToken
		}
		return nil, fmt.Errorf("failed to parse token: %v", err)
	}

	// 验证token
	if claims, ok := token.Claims.(*Claims); ok && token.Valid {
		// 更新缓存
		tokenCache.Store(tokenString, struct {
			Claims *Claims
			Expiry time.Time
		}{
			Claims: claims,
			Expiry: time.Now().Add(tokenCacheDuration),
		})
		return claims, nil
	}

	return nil, ErrInvalidToken
}

// ParseToken 解析JWT令牌
func ParseToken(tokenString string) (jwt.MapClaims, error) {
	token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
		return []byte(getJWTSecret()), nil
	})

	if err != nil {
		return nil, err
	}

	if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
		return claims, nil
	}

	return nil, errors.New("invalid token")
}

// GeneratePreLoginToken 生成预登录令牌
func GeneratePreLoginToken(userID int64, username string) (string, error) {
	// 设置过期时间为5分钟
	expirationTime := time.Now().Add(5 * time.Minute)
	issuedAt := time.Now()
	notBefore := issuedAt

	// 创建JWT声明
	claims := &PreLoginClaims{
		UserID:   userID,
		Username: username,
		RegisteredClaims: jwt.RegisteredClaims{
			ExpiresAt: jwt.NewNumericDate(expirationTime),
			IssuedAt:  jwt.NewNumericDate(issuedAt),
			NotBefore: jwt.NewNumericDate(notBefore),
			Issuer:    "cross-pay-go",
			Subject:   fmt.Sprintf("%d", userID),
		},
	}

	// 创建token
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)

	// 使用密钥签名token
	tokenString, err := token.SignedString([]byte(config.GlobalConfig.JWT.Secret))
	if err != nil {
		return "", fmt.Errorf("failed to sign pre-login token: %v", err)
	}

	return tokenString, nil
}

// ValidatePreLoginToken 验证预登录令牌
func ValidatePreLoginToken(tokenString string) (*PreLoginClaims, error) {
	// 检查token是否为空
	if tokenString == "" || tokenString == "undefined" {
		return nil, fmt.Errorf("pre-login token is empty or undefined")
	}

	// 解析token
	token, err := jwt.ParseWithClaims(tokenString, &PreLoginClaims{}, func(token *jwt.Token) (interface{}, error) {
		// 验证签名方法
		if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
			return nil, fmt.Errorf("invalid signing method: %v", token.Method.Alg())
		}
		return []byte(config.GlobalConfig.JWT.Secret), nil
	})

	if err != nil {
		switch {
		case errors.Is(err, jwt.ErrTokenExpired):
			return nil, ErrExpiredToken
		case errors.Is(err, jwt.ErrTokenMalformed):
			return nil, fmt.Errorf("token is malformed: %v", err)
		case errors.Is(err, jwt.ErrTokenSignatureInvalid):
			return nil, fmt.Errorf("token signature is invalid: %v", err)
		default:
			return nil, fmt.Errorf("failed to parse pre-login token: %v", err)
		}
	}

	// 验证token
	if claims, ok := token.Claims.(*PreLoginClaims); ok && token.Valid {
		return claims, nil
	}

	return nil, ErrInvalidToken
}

// ClearTokenCache 清除指定token的缓存
func ClearTokenCache(tokenString string) {
	tokenCache.Delete(tokenString)
}
